
//
// 110421 - AH - Created.
// 110422 - AH - Implemented scaling to ratio and added missing java doc.
// 110602 - AH - (Hector) scale(Image, int, int, ImageObserver) no longer throw
//               IOException but may return null.
//

package org.soekrat.gui.swing.synesthesia;

import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import java.io.IOException;
import java.net.URL;

import javax.imageio.ImageIO;

/**
 * <p>
 *   Utility for working with images.
 * </p>
 * @author Arne Halvorsen (AH)
 * @author Laetitia Le Chatton (LLC) 
 */
public final class ImageUtil 
{
  private ImageUtil(){} // Utility pattern dictates private constructor.
  
  /**
   * <p>
   *   Loads image using given URL and scale it.
   * </p> 
   * @param url URL for image location.
   * @param w   Width to scale to. If {@code -1} computes width to keep 
   *            original's ratio given height {@code h}.
   * @param h   Height to scale to. If {@code -1} computes height to keep 
   *            original's ratio given width {@code w}.
   * @param io  {@link ImageObserver} waiting for image to be loaded.
   * @return Scaled image.
   * @throws IllegalArgumentException If {@code w} and {@code h} both are
   *         {@code -1}.
   * @throws IllegalArgumentException If {@code w==0 || w<-1 || h==0 || h<-1}.
   */
  public static BufferedImage scale(URL url, int w, int h, ImageObserver io)
  {
    try
    {
      BufferedImage bi=ImageIO.read(url);
      return scale(bi, w, h, io);
    }
    catch (IOException iox)
    {
      return null;
    }
  }
  
  /**
   * <p>
   *   Scale image.
   * </p> 
   * @param image Image to scale.
   * @param w     Width to scale to. If {@code -1} computes width to keep 
   *              original's ratio given height {@code h}.
   * @param h     Height to scale to. If {@code -1} computes height to keep 
   *              original's ratio given width {@code w}.
   * @param io    {@link ImageObserver} waiting for image to be loaded.
   * @return Scaled image or {@code null} if could not produce.
   * @throws IllegalArgumentException If {@code w} and {@code h} both are
   *         {@code -1}.
   * @throws IllegalArgumentException If {@code w==0 || w<-1 || h==0 || h<-1}.
   */
  public static BufferedImage scale(Image image, int w, int h, ImageObserver io)
  {
    if (io==null)
    {
      throw new NullPointerException("io");
    }
    
    if (image==null)
    {
      throw new NullPointerException("image");
    }
    
    if (w==0)
    {
      throw new IllegalArgumentException("w==0");
    }
    if (h==0)
    {
      throw new IllegalArgumentException("h==0");
    }
    
    if (w==-1 && h==-1)
    {
      throw new IllegalArgumentException("w and h both -1");
    }
    
    if (w<-1)
    {
      throw new IllegalArgumentException("w<-1 : "+w);
    }
    if (h<-1)
    {
      throw new IllegalArgumentException("h<-1 : "+h);
    }
    
    int nw=w;
    int nh=h;
    
    int ow=image.getWidth(io);
    int oh=image.getHeight(io);
    
    if (nh==-1)
    {
      nh=getHeightKeepRatio(nw, ow, oh);
    }
    else if (nw==-1)
    {
      nw=getWidthtKeepRatio(nh, ow, oh);
    }
    
    BufferedImage scaledImage=new BufferedImage(nw, nh, 
      BufferedImage.TYPE_INT_ARGB);
    
    Graphics2D g2 = scaledImage.createGraphics();
    g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
      RenderingHints.VALUE_INTERPOLATION_BILINEAR);
    
    g2.drawImage(image, 0, 0, nw, nh, null);
    
    g2.dispose();
    
    return scaledImage;
  }
  
  /**
   * <p>
   *   Computes height given wanted width to keep ratio.
   * </p>
   * @param nw New width.
   * @param w  Existing width defining ratio.
   * @param h  Existing height defining ration. 
   * @return Height.
   */
  private static int getHeightKeepRatio(int nw, int w, int h)
  {
    if (nw<1)
    {
      throw new IllegalArgumentException("nw<1 : "+nw);
    }
    if (w<1)
    {
      throw new IllegalArgumentException("w<1 : "+w);
    }
    if (h<1)
    {
      throw new IllegalArgumentException("h<1 : "+h);
    }
    
    if (nw==w) return h;
    
    float r=(float)h/(float)w;
    float nwf=(float)nw;
    float nh=nwf*r;
    
    return (int)nh;
  }
  
  /**
   * <p>
   *   Computes width given wanted width to keep ratio.
   * </p>
   * @param nh New height.
   * @param w  Existing width defining ratio.
   * @param h  Existing height defining ration. 
   * @return Width.
   */
  private static int getWidthtKeepRatio(int nh, int w, int h)
  {
    if (nh<1)
    {
      throw new IllegalArgumentException("nh<1 : "+nh);
    }
    if (w<1)
    {
      throw new IllegalArgumentException("w<1 : "+w);
    }
    if (h<1)
    {
      throw new IllegalArgumentException("h<1 : "+h);
    }
    
    if (nh==h) return w;
    
    float r=(float)h/(float)w;
    float nhf=(float)nh;
    float nw=nhf/r;
    
    return (int)nw;
  }
  
}
